package com.hoolai.synroom.config;

import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.google.protobuf.GeneratedMessage;

public class CmdsConfig {
    private ReqCmdProperties[] reqCmds;

    private final Map<String, Integer> respCmdsMap = new HashMap<String, Integer>();
    
    private final Map<Integer, String> cmdsRespMap = new HashMap<Integer, String>();

    private final Logger logger;

    public enum CmdDomain {
        direct, global, synRoom
    }

    //com/hoolai/sango/synfight/reqCMD.xml
    //com/hoolai/sango/synfight/respCMD.xml
    //synfight
    public CmdsConfig(String reqCmdUrl, String respCmdUrl, Logger logger) {
        this.logger = logger;
        init(reqCmdUrl, respCmdUrl);
    }

    private void init(String reqCmdUrl, String respCmdUrl) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        InputStream reqCmdInputStream = loader.getResourceAsStream(reqCmdUrl);
        InputStream respCmdInputStream = loader.getResourceAsStream(respCmdUrl);
        try {
            initReqCmd(reqCmdInputStream);
            initRespCmd(respCmdInputStream);
        } catch (DocumentException e) {
            logger.error("load cmd.xml fail", e);
            throw new Error(e);
        } catch (IllegalArgumentException e) {
            logger.error("load cmd.xml fail", e);
            throw new Error(e);
        } catch (SecurityException e) {
            logger.error("load cmd.xml fail", e);
            throw new Error(e);
        } catch (InstantiationException e) {
            logger.error("load cmd.xml fail", e);
            throw new Error(e);
        } catch (IllegalAccessException e) {
            logger.error("load cmd.xml fail", e);
            throw new Error(e);
        } catch (ClassNotFoundException e) {
            logger.error("load cmd.xml fail", e);
            throw new Error(e);
        } catch (InvocationTargetException e) {
            logger.error("load cmd.xml fail", e);
            throw new Error(e);
        } catch (NoSuchMethodException e) {
            logger.error("load cmd.xml fail", e);
            throw new Error(e);
        }
    }

    @SuppressWarnings("unchecked")
    public void initRespCmd(InputStream inputStream) throws DocumentException, InstantiationException,
            IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException,
            InvocationTargetException, NoSuchMethodException {
        SAXReader reader = new SAXReader();
        Document doc = reader.read(inputStream);
        List<Element> elements = doc.getRootElement().elements();
        for (Element cmdTypeEle : elements) {
            respCmdsMap.put(cmdTypeEle.elementText("ProtoName"), Integer.parseInt(cmdTypeEle.elementText("ID")));
            cmdsRespMap.put(Integer.parseInt(cmdTypeEle.elementText("ID")), cmdTypeEle.elementText("ProtoName"));
        }
    }

    @SuppressWarnings("unchecked")
    public void initReqCmd(InputStream inputStream) throws DocumentException, InstantiationException,
            IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException,
            InvocationTargetException, NoSuchMethodException {
        SAXReader reader = new SAXReader();
        Document doc = reader.read(inputStream);
        List<Element> elements = doc.getRootElement().elements();
        reqCmds = initReqCmds(elements);
        for (Element cmdTypeEle : elements) {
            String protoName = cmdTypeEle.elementText("ProtoName");
            String serviceName = cmdTypeEle.elementText("Service");
            String methodName = cmdTypeEle.elementText("Method");
            String domain = cmdTypeEle.elementText("Region");
            Integer index = Integer.valueOf(cmdTypeEle.elementText("Index"));
            CmdDomain cmdDomain = CmdDomain.valueOf(domain);

            FastClass fastClass = FastClass.create(Class.forName(protoName));
            FastMethod method = fastClass.getMethod("getDefaultInstance", null);
            GeneratedMessage proto = (GeneratedMessage) method.invoke(null, null);

            if (proto == null) {
                logger.error("load cmd.xml fail, can't find the proto: name = " + protoName);
                throw new Error("load cmd.xml fail, can't find the proto: name = " + protoName);
            }
            ReqCmdProperties cmdProperties = new ReqCmdProperties(protoName, serviceName, methodName, proto, cmdDomain,
                    index);

            reqCmds[index] = cmdProperties;
        }
    }

    private ReqCmdProperties[] initReqCmds(List<Element> elements) {
        return new ReqCmdProperties[Integer.parseInt(elements.get(elements.size() - 1).elementText("Index")) + 1];
    }

    public ReqCmdProperties[] getReqCmds() {
        return reqCmds;
    }

    public Integer getRespCmdId(String protoName) {
        return respCmdsMap.get(protoName);
    }
    
    public String getRespString(int id) {
        return cmdsRespMap.get(id);
    }

}
